package com.hmal.geteway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * @author wuJiaWei
 * @version 1.0
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class DynamicRouteLoader {
    private final RouteDefinitionWriter writer;
    private final NacosConfigManager nacosConfigManager;
    private final String dataId = "gateway-routes.json";
    private final String group = "DEFAULT_GROUP";
    private final Set<String> routeIds = new HashSet<>();// 保存更新过的路由id

    @PostConstruct // 初始化时执行该方法
    public void initRouteConfigListener() throws NacosException {
        //1.项目启动时,先拉取一次配置,并且添加配置监听器
        String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                //2.配置更新时,重新加载路由规则
                updateConfigInfo(configInfo);
            }
        });
        //3.第一次读取到配置,也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    /**
     * 更新路由配置信息
     * @param configInfo 配置信息
     */
    public void updateConfigInfo(String configInfo){
        log.debug("监听到路由配置变更，{}", configInfo);
        //1.解析配置信息,转为RouteDefinition对象
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        //2.删除旧的路由表
        routeIds.forEach(routeId -> {
            //2.1删除旧的路由表
            writer.delete(Mono.just(routeId)).subscribe();
        });
        routeIds.clear();//清空旧的路由id
        //3.更新路由表
        routeDefinitions.forEach(routeDefinition -> {
            //3.1保存新的路由表
            writer.save(Mono.just(routeDefinition)).subscribe();
            //3.2保存路由id
            routeIds.add(routeDefinition.getId());
        });
        log.info("路由配置更新完成");
    }
}
